package com.atlc.preview.project.net.server;

import com.atlc.preview.project.model.Examine;
import com.atlc.preview.project.model.Pages;
import com.atlc.preview.project.model.Status;
import com.atlc.preview.project.utils.FileAnalyseUtils;
import javafx.stage.FileChooser;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.Timer;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 梁冲
 * @version 1.0
 * @email 1357042069@qq.com
 * @date 2021/12/01
 */
public class Server {

    private static List<Examine> lists = new ArrayList<>();

    private static String[][] rowData = new String[50][4];

    private String[] columnNames = {"序号", "姓名", "已答题数", "操作状态"};

    private String pages = "";

    private Pages backup;

    private ServerSocket serverSocket;

    private boolean finishLoadPages = false;

    private boolean finishLoadExamine = false;

    private int upCount = 0;

    private int counter = 0;

    private static Map<String, Status> record = new HashMap<>();

    static {
        try {
            int index = 0;
            for (Examine list : lists) {
                rowData[index][0] = (index + 1) + "";
                rowData[index][1] = list.getName();
                record.put(list.getName(), new Status("0"));
                rowData[index][2] = "0";
                rowData[index][3] = "未登录";
                index++;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Server(int port) throws Exception {
        initFrame();
        JOptionPane.showMessageDialog(new JLabel(), "请监考员先在对应地方选择试卷后，再让考生登录考试！");
        initServer(port);
    }

    private void initFrame() {
        new ServerFrame();
    }

    public static void main(String[] args) throws Exception {
        new Server(8010);
    }


    private static boolean check(Examine examine) {
        return lists.stream().filter(e1 -> e1.getId().equals(examine.getId()) && e1.getName().equals(examine.getName()) && e1.getIdentity().equals(examine.getIdentity())).count() == 1;
    }

    public void initServer(int port) {
        try {
            serverSocket = new ServerSocket(port);
            while (true) {
                Socket socket = serverSocket.accept();
                System.out.println(socket.getPort() + "连接");
                if (socket.isConnected()) {
                    // 将套接字保存到一个集合当中里面
                    Thread thread = new Thread(new Handler(socket));
                    thread.start();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (serverSocket != null) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private class Handler implements Runnable {

        private Socket socket;

        private Status status = new Status("0");

        public long time = 3600;

        public Handler(Socket socket) {
            this.socket = socket;
        }

        private BufferedOutputStream getWriter(Socket socket) throws IOException {
            OutputStream socketOut = socket.getOutputStream();
            BufferedOutputStream buff = new BufferedOutputStream(socketOut);
            return buff;
        }

        private BufferedInputStream getReader(Socket socket) throws IOException {
            InputStream socketIn = socket.getInputStream();
            return new BufferedInputStream(socketIn);
        }

        @Override
        public void run() {
            try {
                BufferedInputStream bis = getReader(socket);
                BufferedOutputStream bos = getWriter(socket);
                if (socket.isConnected()) {
                    while (true) {
                        byte[] bytes = new byte[1024];
                        bis.read(bytes);
                        String msg = new String(bytes).trim();
                        System.out.println(msg);
                        String[] params = msg.split(" ");
                        switch (params[0]) {
                            case "001":
                                doCheck(params[1].split("@"), bos);
                                break;
                            case "002":
                                doSend(bos);
                                break;
                            case "003":
                                doRecord(params, bos);
                                break;
                            case "005":
                                doHeartBeat(bos, params);
                                break;
                            case "006":
                                doSaveScore(params[1], bos);
                                break;
                            default:
                                doConnect();
                                break;
                        }
                    }
                }
            } catch (Exception e) {
                if (!"-1".equals(status.getStatus())) {
                    status.setStatus("-2");
                    record.put(Thread.currentThread().getName(), status);
                }
                e.printStackTrace();
            }
        }

        private void doConnect() {
            if (!"-1".equals(status.getStatus())) {
                if ("-2".equals(status.getStatus())) {
                    return;
                }
                status.setStatus("-2");
            }
        }

        private void doSaveScore(String score, BufferedOutputStream bos) throws IOException {
            status.setScore(Integer.parseInt(score));
            status.setStatus("-1");
            record.put(Thread.currentThread().getName(), status);
            bos.write("106 1 保存到服务器成功！".getBytes());
            bos.flush();
        }


        private void doCheck(String[] params, BufferedOutputStream bos) throws IOException {
            if ("null".equals(params[1])) {
                byte[] response = "100 0 收到信息，马上会验证！".getBytes();
                bos.write(response);
                bos.flush();
                return;
            }
            if (checkExamineInfo(params)) {
                status = record.get(params[1]);
                if ("-1".equals(status.getStatus())) {
                    bos.write(("101 -1 该账号已交卷！").getBytes());
                    bos.flush();
                    return;
                } else if ("1".equals(status.getStatus())) {
                    bos.write(("101 -2 该账号已登录！").getBytes());
                    bos.flush();
                    return;
                }
                Thread.currentThread().setName(params[1]);
                if (!"".equals(status.getAnswer())) {
                    status.setStatus("1");
                    record.put(params[1], status);
                    String answer = status.getAnswer();
                    bos.write(("107~" + pages + "~" + answer + "~" + (status.getTime())).getBytes());
                    bos.flush();
                    return;
                }
                status.setStatus("1");
                record.put(params[1], status);
                byte[] response = "101 1 身份验证成功！".getBytes();
                bos.write(response);
                bos.flush();
            } else {
                byte[] response = "101 0 身份验证失败！".getBytes();
                bos.write(response);
                bos.flush();
            }
        }

        private void doHeartBeat(BufferedOutputStream out, String[] params) throws IOException {
            if (!"-1".equals(status.getStatus())) {
                status.setStatus("1");
                status.setTime(Integer.parseInt(params[1]) + 5);
                record.put(Thread.currentThread().getName(), status);
                out.write("105 @ 连接正常！".getBytes());
                out.flush();
            }
        }

        private void doSend(BufferedOutputStream bos) throws IOException {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if ("".equals(pages)) {
                bos.write("102 0 请求试卷成功！,但是服务端还没开始选试卷呢！".getBytes());
                bos.flush();
                return;
            }
            bos.write("102 1 请求试卷成功！".getBytes());
            bos.flush();
            bos.write(("103 " + pages).getBytes());
            bos.flush();
        }

        private void doRecord(String[] params, BufferedOutputStream bos) throws IOException {
            StringBuilder builder = new StringBuilder();
            if (!"-1".equals(status.getStatus())) {
                status.setStatus("1");
                record.put(Thread.currentThread().getName(), status);
                for (String param : params) {
                    if ("003".equals(param)) {
                        continue;
                    }
                    param = param.replace("@", "null");
                    builder.append(param);
                    builder.append(" ");
                }
                System.out.println(builder);
                status.setAnswer(builder.toString());
            }
            bos.write("104 1 保存状态成功！".getBytes());
            bos.flush();
        }

        private boolean checkExamineInfo(String[] params) {
            return lists.stream().filter(examine -> examine.getName().equals(params[1]) && examine.getId().equals(params[0]) && examine.getIdentity().equals(params[2])).count() == 1;
        }

    }


    class ServerFrame extends JFrame {

        private Font font = new Font("微软雅黑", Font.BOLD, 25);

        private JLabel testName = new JLabel("Java基础知识测试");
        private JLabel reference = new JLabel("参考人数：" + lists.size());
        private JLabel questionNumber = new JLabel("题目总数：" + (backup != null ? backup.getProblemList().size() : 0));
        private JLabel submitNumber = new JLabel("已提交：" + upCount);

        // 表头（列名）
        private DefaultTableModel model;
        // 创建一个表格，指定 表头 和 所有行数据
        JTable table = new JTable(model = new DefaultTableModel(rowData, columnNames));
        JMenuBar menuBar = new JMenuBar();//创建菜单栏对象
        JMenu testPaper = new JMenu("试卷管理");
        JMenu examine = new JMenu("考生管理");
        JMenu grade = new JMenu("成绩管理");
        JMenuItem addPaper = new JMenuItem("添加试卷");
        JMenuItem loadExamineStatus = new JMenuItem("刷新考生状态");
        JMenuItem loadExamine = new JMenuItem("载入考生信息");
        JMenuItem saveGrade = new JMenuItem("保存成绩");

        BorderLayout bl = new BorderLayout(50, 50);
        JPanel paperChoose = new JPanel();
        JPanel testInformation = new JPanel(bl);
        JPanel studentInformation = new JPanel();
        JPanel state = new JPanel();
        JPanel JPTime = new JPanel();

        JTable paperList = new JTable();

        JButton sure = new JButton("确定");
        JButton cancel = new JButton("取消");

        FileChooser fileChooser = new FileChooser();

        JScrollPane scrollPane = new JScrollPane(table);


        ServerFrame() {
            this.setTitle("监控端");
            this.setSize(1500, 800);
            this.setLocationRelativeTo(null);  //将此窗口置于屏幕的中央
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            this.setLayout(new BorderLayout(20, 20));

//        设置菜单
            setJMenuBar(menuBar);
            menuBar.add(testPaper);
            menuBar.add(examine);
            menuBar.add(grade);
            testPaper.add(addPaper);
            examine.add(loadExamineStatus);
            examine.add(loadExamine);
            grade.add(saveGrade);

//        设置字体
            sure.setFont(font);
            cancel.setFont(font);
            testName.setFont(font);
            reference.setFont(font);
            questionNumber.setFont(font);
            submitNumber.setFont(font);
            table.setFont(font);
            testPaper.setFont(font);
            examine.setFont(font);
            grade.setFont(font);
            addPaper.setFont(font);
            loadExamine.setFont(font);
            loadExamineStatus.setFont(font);
            saveGrade.setFont(font);
            fileChooser.setTitle("添加试卷");

            paperChoose.add(paperList);
            this.add(paperChoose, BorderLayout.WEST);

            testInformation.add(testName, BorderLayout.NORTH);

            time();
            testInformation.add(JPTime, BorderLayout.EAST);

            state.add(reference);
            state.add(questionNumber);
            state.add(submitNumber);
            testInformation.add(state, BorderLayout.CENTER);

            this.add(testInformation, BorderLayout.NORTH);

            // 设置表头
            table.getTableHeader().setFont(font);// 设置表头名称字体样式
            table.getTableHeader().setForeground(Color.RED);// 设置表头名称字体颜色
            table.getTableHeader().setResizingAllowed(true);// 设置允许手动改变列宽
            table.getTableHeader().setReorderingAllowed(false);// 设置不允许拖动重新排序各列
            table.setRowHeight(50);//设置行高
            table.setPreferredScrollableViewportSize(new Dimension(1400, 400));// 设置滚动面板视口大小（超过该大小的行数据，需要拖动滚动条才能看到）// 把 表格 放到 滚动面板 中（表头将自动添加到滚动面板顶部）
            studentInformation.add(scrollPane);// 添加 滚动面板 到 内容面板
            this.add(studentInformation, BorderLayout.CENTER);

            setVisible(true);

            judgePaper();
        }

        //    设置监听器
        public void judgePaper() {
//        添加试卷
            addPaper.addActionListener(e -> new MyFileChooser(1, loadExamineStatus));
//        保存成绩
            saveGrade.addActionListener(e -> {
                Set<Map.Entry<String, Status>> entries = record.entrySet();
                File file = new File("examineStatus.txt");
                if (!file.exists()) {
                    try {
                        file.createNewFile();
                    } catch (IOException ioException) {
                        ioException.printStackTrace();
                    }
                }
                BufferedOutputStream bos = null;
                try {
                    bos = new BufferedOutputStream(new FileOutputStream(file));
                } catch (FileNotFoundException fileNotFoundException) {
                    fileNotFoundException.printStackTrace();
                }
                for (Map.Entry<String, Status> entry : entries) {
                    String name = entry.getKey();
                    Status status = entry.getValue();
                    String answer = status.getAnswer();
                    answer.replaceAll(" ", "@");
                    String stuStatus = status.getStatus();
                    int time = status.getTime();
                    int score = status.getScore();
                    String str = name + " " + score + " " + stuStatus + " " + time + " " + answer + "\n";
                    byte[] bytes = str.getBytes();
                    try {
                        bos.write(bytes);
                        bos.flush();
                    } catch (IOException ioException) {
                        ioException.printStackTrace();
                    }
                }
                JOptionPane.showMessageDialog(new JLabel(), "保存考生信息成功！");
            });
            loadExamineStatus.addActionListener(e -> {
                upCount = 0;
                int index = 0;
                if (finishLoadExamine) {
                    for (Examine list : lists) {
                        rowData[index][0] = (index + 1) + "";
                        rowData[index][1] = list.getName();
                        Status status = record.get(list.getName());
                        if ("0".equals(status.getStatus())) {
                            rowData[index][3] = "未登录";
                        } else if ("1".equals(status.getStatus())) {
                            rowData[index][3] = "答题中";
                        } else if ("-1".equals(status.getStatus())) {
                            rowData[index][3] = "已交卷";
                            upCount++;
                        } else {
                            rowData[index][3] = "断开连接";
                        }
                        if ("".equals(status.getAnswer())) {
                            rowData[index][2] = "0";
                        } else {
                            String answer = status.getAnswer();
                            answer = answer.replaceAll("null", "");
                            int count = answer.split(" ").length;
                            rowData[index][2] = (count) + "";
                        }
                        index++;
                    }
                    reference.setText("参考人数：" + lists.size());
                    model.setDataVector(rowData, columnNames);
                }
                if (finishLoadPages) {
                    questionNumber.setText("题目总数：" + backup.getProblemList().size());
                }
                submitNumber.setText("已提交：" + upCount);
            });
            loadExamine.addActionListener(e -> {
                new MyFileChooser(2, loadExamineStatus);
            });
        }

        //     定时器
        private void time() {
            java.util.Timer timer = new Timer();
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    if (counter++ % 10 == 0) {
                        loadExamineStatus.doClick();
                    }
                }
            }, 0, 1000);  // 延时0秒触发，以后每间隔1秒循环触发
        }
    }

    class MyFileChooser extends JFrame implements ActionListener {

        private JFileChooser jfc = new JFileChooser(new File("."));

        private JButton bOpen;

        private int chooseStatus;

        private JMenuItem item;

        public MyFileChooser(int status, JMenuItem j) {
            item = j;
            chooseStatus = status;
            bOpen = new JButton("载入文件");
            bOpen.addActionListener(this);
            this.add(bOpen);
            this.setTitle("文件选择器");
            this.setLayout(new FlowLayout(FlowLayout.CENTER, 15, 10));
            this.setSize(300, 100);
            this.setLocationRelativeTo(null);
            this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            this.setVisible(true);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            JButton jbt = (JButton) e.getSource();
            //1.点击bOpen要做的事为
            if (jbt == bOpen) {
                int status = jfc.showOpenDialog(this);
                if (status != JFileChooser.APPROVE_OPTION) {
                    JOptionPane.showMessageDialog(new JLabel(), "没有选中文件！");
                }
                File file = jfc.getSelectedFile();
                //被选中的文件保存为文件对象
                if (chooseStatus == 1 && !finishLoadPages) {
                    analysePagesInfo(file);
                } else if (chooseStatus == 2 && !finishLoadExamine) {
                    analyseExamineInfo(file);
                }
            }
        }

        private void analyseExamineInfo(File file) {
            BufferedReader bufferedReader = null;
            try {
                bufferedReader = new BufferedReader(new FileReader(file));
                List<String> examineInfo = bufferedReader.lines().collect(Collectors.toList());
                for (String e : examineInfo) {
                    String[] split = e.split(" ");
                    Examine examine = new Examine(split[0], split[1], split[2]);
                    Status status = new Status("0");
                    record.put(examine.getName(), status);
                    lists.add(examine);
                }
                if (lists == null) {
                    JOptionPane.showMessageDialog(new JLabel(), "载入考生失败！");
                } else {
                    JOptionPane.showMessageDialog(new JLabel(), "载入考生成功！");
                    finishLoadExamine = true;
                    item.doClick();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        private void analysePagesInfo(File file) {
            backup = FileAnalyseUtils.analyseTestPaper(file);
            BufferedReader bufferedReader = null;
            try {
                bufferedReader = new BufferedReader(new FileReader(file));
            } catch (FileNotFoundException fileNotFoundException) {
                fileNotFoundException.printStackTrace();
            }
            List<String> list = bufferedReader.lines().collect(Collectors.toList());
            StringBuilder builder = new StringBuilder();
            for (String s : list) {
                builder.append(s);
                builder.append("@");
            }
            pages = builder.substring(0, builder.length() - 4);
            if (pages != null) {
                JOptionPane.showMessageDialog(new JLabel(), "加载试卷成功！");
                finishLoadPages = true;
                item.doClick();
            } else {
                JOptionPane.showMessageDialog(new JLabel(), "加载试卷失败！不支持该文件格式！");
            }
        }
    }
}
